package fight

import (
	"encoding/binary"
)

type WrapMsg struct {
	// 协议号
	Cmd uint16
	// 解包后/打包前的数据
	Msg Msger
	// 二进制包, cmd:2+payload:Len
	Bs []byte
	// 登陆包所包含的客户端信息
	C *Conn
}

func GetCmdByBS(bs []byte) uint16 {
	return binary.BigEndian.Uint16(bs)
}

func NewC2SWrapMsg(cmd uint16, mt MsgerTyper) (*WrapMsg, error) {
	msger, err := mt.C2SMsgerType(cmd)
	if err != nil {
		return nil, err
	}
	return &WrapMsg{
		Cmd: cmd,
		Msg: msger,
	}, nil
}

func NewS2CWrapMsg(cmd uint16, mt MsgerTyper) (*WrapMsg, error) {
	msger, err := mt.S2CMsgerType(cmd)
	if err != nil {
		return nil, err
	}
	return &WrapMsg{
		Cmd: cmd,
		Msg: msger,
	}, nil
}

// bs -> msg
func (wm *WrapMsg) NewDecodeWrapMsg(bs []byte) error {
	err := wm.Msg.Decode(bs[CMD_BYTES:])
	if err != nil {
		return err
	}
	// 因为需要支持解包bs为msg后回收bs的内存, 所以这里不必赋值到wm.Bs中
	// wm.Bs = bs
	return nil
}

// msg -> bs
func (wm *WrapMsg) NewEncodeWrapMsg() error {
	payload, err := wm.Msg.Encode()
	if err != nil {
		return err
	}
	wm.Bs = packBs(wm.Cmd, payload)
	return nil
}

func packBs(cmd uint16, payload []byte) []byte {
	payloadSize := len(payload)
	bs := make([]byte, 2, 2+payloadSize)
	binary.BigEndian.PutUint16(bs, cmd)
	bs = append(bs, payload...)
	return bs
}

type MsgerTyper interface {
	C2SMsgerType(uint16) (Msger, error)
	S2CMsgerType(uint16) (Msger, error)
}

// Msger: 数据包结构父接口, 所有的数据包都以Msger接口的形式存在于WrapMsg接口当中
type Msger interface {
	Decode([]byte) error
	Encode() ([]byte, error)
	LoginReqMsger
	LoginAckMsger
	LoadAckMsger
	TimelineAckMsger
	UploadFrameMsger
	BroadcastFrameMsger
	SettleReqMsger
	SettleAckMsger
	NullMsger
}

// 登陆请求子接口
type LoginReqMsger interface {
	GetLoginReqUserId() int32
	SetLoginReqUserId(int32)
	GetLoginReqFightId() uint64
	SetLoginReqFightId(uint64)
}

// 登陆回包子接口
type LoginAckMsger interface {
	GetLoginAckRet() int32
	SetLoginAckRet(int32)
	GetLoginAckGameState() int32
	SetLoginAckGameState(int32)
}

// 加载回包子接口
type LoadAckMsger interface {
	GetLoadAckRet() int32
	SetLoadAckRet(int32)
}

// 时间轴启动回包子接口
type TimelineAckMsger interface {
	GetTimelineAckRet() int32
	SetTimelineAckRet(int32)
}

// 帧指令上传子接口
type UploadFrameMsger interface {
	GetUploadFrameCmdId() int32
	SetUploadFrameCmdId(int32)
	GetUploadFrameId() int32
	SetUploadFrameId(int32)
	SetUploadFrameData(interface{})
}

// 广播帧操作数据子接口
type BroadcastFrameMsger interface {
	GetBroadcastFrameId() int32
	SetBroadcastFrameId(int32)
	GetBroadcastFrameTimeline() int32
	SetBroadcastFrameTimeline(int32)
	AppendBroadcastFrameDatas(int32, UploadFrameMsger) // UserId, UploadFrameMsger
}

// 结算请求子接口
type SettleReqMsger interface {
	SetSettleReqResult(interface{})
}

// 战斗结算广播子接口
type SettleAckMsger interface {
	AppendSettleAckResults(int32, SettleReqMsger) // UserId, SettleReqMsger
}

// 空包子接口
type NullMsger interface {
}

// 实现Msger接口的父类, 自定义的结构需要包含该父类
type FsdBaseMsg struct {
}

func (*FsdBaseMsg) Decode([]byte) error     { return nil }
func (*FsdBaseMsg) Encode() ([]byte, error) { return nil, nil }

func (*FsdBaseMsg) GetLoginReqUserId() int32   { return 0 }
func (*FsdBaseMsg) SetLoginReqUserId(int32)    {}
func (*FsdBaseMsg) GetLoginReqFightId() uint64 { return 0 }
func (*FsdBaseMsg) SetLoginReqFightId(uint64)  {}

func (*FsdBaseMsg) GetLoginAckRet() int32       { return 0 }
func (*FsdBaseMsg) SetLoginAckRet(int32)        {}
func (*FsdBaseMsg) GetLoginAckGameState() int32 { return 0 }
func (*FsdBaseMsg) SetLoginAckGameState(int32)  {}

func (*FsdBaseMsg) GetLoadAckRet() int32 { return 0 }
func (*FsdBaseMsg) SetLoadAckRet(int32)  {}

func (*FsdBaseMsg) GetTimelineAckRet() int32 { return 0 }
func (*FsdBaseMsg) SetTimelineAckRet(int32)  {}

func (*FsdBaseMsg) GetUploadFrameCmdId() int32     { return 0 }
func (*FsdBaseMsg) SetUploadFrameCmdId(int32)      {}
func (*FsdBaseMsg) GetUploadFrameId() int32        { return 0 }
func (*FsdBaseMsg) SetUploadFrameId(int32)         {}
func (*FsdBaseMsg) SetUploadFrameData(interface{}) {}

func (*FsdBaseMsg) GetBroadcastFrameId() int32                        { return 0 }
func (*FsdBaseMsg) SetBroadcastFrameId(int32)                         {}
func (*FsdBaseMsg) GetBroadcastFrameTimeline() int32                  { return 0 }
func (*FsdBaseMsg) SetBroadcastFrameTimeline(int32)                   {}
func (*FsdBaseMsg) AppendBroadcastFrameDatas(int32, UploadFrameMsger) {}

func (*FsdBaseMsg) SetSettleReqResult(interface{})               {}
func (*FsdBaseMsg) AppendSettleAckResults(int32, SettleReqMsger) {}

// 预处理部分回包
func InitS2cMsg() {
	init_CmdLogin()
	init_CmdAllLogined()
	init_CmdLoading()
	init_CmdAllLoad()
	init_CmdTimeline()
	init_CmdSettle()
	init_CmdHeartBeat()
}

var (
	PreS2CMsgFightLoginAckFail    *WrapMsg
	PreS2CMsgFightAllLogined      *WrapMsg
	PreS2CMsgFightLoadAckOK       *WrapMsg
	PreS2CMsgFightLoadAckFail     *WrapMsg
	PreS2CMsgFightAllLoaded       *WrapMsg
	PreS2CMsgFightTimelineAckOK   *WrapMsg
	PreS2CMsgFightTimelineAckFail *WrapMsg
	PreS2CMsgFightSettleAckFail   *WrapMsg
	PreS2CMsgHeartBeat            *WrapMsg
)

func init_CmdLogin() {
	var err error
	PreS2CMsgFightLoginAckFail, err = NewS2CWrapMsg(PT_CMD_LOGIN, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightLoginAckFail.Msg.SetLoginAckRet(PT_ACK_RET_FAIL)
	PreS2CMsgFightLoginAckFail.NewEncodeWrapMsg()
}

func init_CmdAllLogined() {
	var err error
	PreS2CMsgFightAllLogined, err = NewS2CWrapMsg(PT_CMD_ALL_LOGINED, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightAllLogined.NewEncodeWrapMsg()
}

func init_CmdLoading() {
	var err error
	PreS2CMsgFightLoadAckOK, err = NewS2CWrapMsg(PT_CMD_LOADING, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightLoadAckOK.Msg.SetLoadAckRet(PT_ACK_RET_OK)
	PreS2CMsgFightLoadAckOK.NewEncodeWrapMsg()

	PreS2CMsgFightLoadAckFail, err = NewS2CWrapMsg(PT_CMD_LOADING, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightLoadAckFail.Msg.SetLoadAckRet(PT_ACK_RET_FAIL)
	PreS2CMsgFightLoadAckFail.NewEncodeWrapMsg()
}

func init_CmdAllLoad() {
	var err error
	PreS2CMsgFightAllLoaded, err = NewS2CWrapMsg(PT_CMD_ALL_LOADED, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightAllLoaded.NewEncodeWrapMsg()
}

func init_CmdTimeline() {
	var err error
	PreS2CMsgFightTimelineAckOK, err = NewS2CWrapMsg(PT_CMD_TIMELINE, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightTimelineAckOK.Msg.SetTimelineAckRet(PT_ACK_RET_OK)
	PreS2CMsgFightTimelineAckOK.NewEncodeWrapMsg()

	PreS2CMsgFightTimelineAckFail, err = NewS2CWrapMsg(PT_CMD_TIMELINE, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightTimelineAckFail.Msg.SetTimelineAckRet(PT_ACK_RET_FAIL)
	PreS2CMsgFightTimelineAckFail.NewEncodeWrapMsg()
}

func init_CmdSettle() {
	var err error
	PreS2CMsgFightSettleAckFail, err = NewS2CWrapMsg(PT_CMD_SETTLE, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgFightSettleAckFail.NewEncodeWrapMsg()
}

func init_CmdHeartBeat() {
	var err error
	PreS2CMsgHeartBeat, err = NewS2CWrapMsg(PT_CMD_HEART_BEAT, Srv.GameProtocoler)
	if err != nil {
		panic("pt init panic")
	}
	PreS2CMsgHeartBeat.NewEncodeWrapMsg()
}
